﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;

/*
* 源码已托管：https://gitee.com/dlgcy/WPFTemplateLib
*/
namespace WPFTemplateLib.Enhance
{
	/// <summary>
	/// 【不可用，会报不支持范围操作】带缓冲的 <see cref="ObservableCollection&lt;T&gt;"/> [实验性]<para/>
	/// Written by JvanLangen（https://www.wenjiangs.com/group/topic-630464.html）
	/// 注意：操作结束要调用 Flush() 方法。
	/// </summary>
	public class BufferedObservableCollection<T> : ObservableCollection<T>
	{
		// Intro: If you remove or add 10000 items your application (listboxes) will be very busy with handling the events raised by the ObservableCollection.
		// The event already supports multiple items. So.....
		// You could buffer the items until it changes the action. So Add actions will be buffered and wil be raised as batch if the 'user' changes action or flushes it.
		// Haven't test it, but you could do something like this:

		// the last action used
		public NotifyCollectionChangedAction? _lastAction = null;
		// the items to be buffered
		public List<T> _itemBuffer = new List<T>();

		/// <summary>
		/// constructor registeres on the CollectionChanged
		/// </summary>
		public BufferedObservableCollection()
		{
			base.CollectionChanged += ObservableCollectionUpdate_CollectionChanged;
		}

		// When the collection changes, buffer the actions until the 'user' changes action or flushes it.
		// This will batch add and remove actions.
		private void ObservableCollectionUpdate_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			// if we have a lastaction, check if it is changed and should be flush else only change the lastaction
			if (_lastAction.HasValue)
			{
				if (_lastAction != e.Action)
				{
					Flush();
					_lastAction = e.Action;
				}
			}
			else
				_lastAction = e.Action;

			if (e.NewItems != null)
			{
				_itemBuffer.AddRange(e.NewItems.Cast<T>());
			}
		}

		/// <summary>
		/// Raise the new event.
		/// </summary>
		protected void RaiseCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			CollectionChanged?.Invoke(sender, e);
		}

		/// <summary>
		/// Don't forget to flush the list when your ready with your action
		/// or else the last actions will not be 'raised'
		/// </summary>
		public void Flush()
		{
			if (_lastAction.HasValue && _itemBuffer.Count > 0)
			{
				RaiseCollectionChanged(this, new NotifyCollectionChangedEventArgs(_lastAction.Value, _itemBuffer));
				_itemBuffer.Clear();
				_lastAction = null;
			}
		}

		// new event
		public override event NotifyCollectionChangedEventHandler CollectionChanged;
	}
}
